package com.corpgovernment.common.aspect;


import com.corpgovernment.common.apollo.CommonApollo;
import com.corpgovernment.common.base.CommonException;
import com.corpgovernment.common.common.CorpBusinessException;
import com.corpgovernment.common.constant.CommonConst;
import com.corpgovernment.common.constant.CommonResponseCodeEnum;
import com.corpgovernment.common.dto.GetTenantInfoDTO;
import com.corpgovernment.common.utils.LogSplicingUtils;
import com.ctrip.corp.obt.auth.client.handler.token.JwtTokenHandler;
import com.ctrip.corp.obt.auth.core.authenticate.BaseClientDetails;
import com.ctrip.corp.obt.generic.core.context.TenantContext;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.EnvironmentHolder;
import com.ctrip.corp.obt.generic.utils.JsonUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.ctrip.corp.obt.metric.Metrics;
import com.ctrip.corp.obt.metric.spectator.api.Id;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Aspect
@Component
@Slf4j
@Order(-1)
public class ExternalCallDataSourceAspect {

    @Autowired
    @Qualifier(value = "commonApollo")
    private CommonApollo commonApollo;

    @Autowired
    private JwtTokenHandler jwtTokenHandler;

    @Pointcut("@annotation(com.corpgovernment.common.annotation.ExternalCallGetTenant)")
    public void addAdvice() {
    }

    Id id = Metrics.REGISTRY.createId("external.request.get.tenant.id.aspect");

    @Around("addAdvice()")
    public Object Interceptor(ProceedingJoinPoint pjp) {
        StringBuilder logContext = new StringBuilder();
        LogSplicingUtils.addLogContext(logContext, "开获取参数中的租户ID");
        LogSplicingUtils.addLogContext(logContext, "操作方法：%s", pjp.getSignature().getName());
        String tenantId = "";
        Boolean executeResult = Boolean.TRUE;
        Instant start = Instant.now();
        try {
            TenantContext.setTenantId(CommonConst.DEFAULT_TENANT_ID);
            tenantId = getTenantIdByCorpId(logContext, getCorpId(pjp, logContext), commonApollo.getTenantAndCorpRelation());

            //支付宝回调 单独逻辑获取参数
            if (StringUtils.isBlank(tenantId)) {
                tenantId = getTenantIdByAlipay(logContext, pjp);
            }

            //最后环节也未按逻辑取到租户ID抛出异常
            TenantContext.setTenantId(Optional.ofNullable(tenantId).orElseThrow(() -> new CommonException("未取到租户ID")));

            BaseClientDetails baseClientDetails = jwtTokenHandler.getRootClientDetailsByTenantId(tenantId);
            if (baseClientDetails == null || StringUtils.isBlank(baseClientDetails.getClientId())) {
                log.error("获取clientid异常,tenantId:{},baseClientDetails:{}", tenantId, JsonUtils.toJsonString(baseClientDetails));
                throw new CorpBusinessException(CommonResponseCodeEnum.QUERY_CLIENTID_ERROR);
            }
            TenantContext.setClientId(baseClientDetails.getClientId());

            LogSplicingUtils.addLogContext(logContext, "最终租户ID为：%s", tenantId);
            return pjp.proceed();
        } catch (Exception e) {
            executeResult = Boolean.FALSE;
            log.error("ExceptionExternalCallDataSourceAspectError:{} 获取租户异常",logContext, e);
        } catch (Throwable e) {
            executeResult = Boolean.FALSE;
            log.error("ThrowableExternalCallDataSourceAspectError:{}请求异常为",logContext, e);
        } finally {
            Instant end = Instant.now();
            exceptionMetric(pjp, executeResult, Duration.between(start, end));
            TenantContext.unset();
            log.info("ExternalCallGetTenant:{}", logContext.toString());
        }

        return null;
    }


    private void exceptionMetric(ProceedingJoinPoint pjp, Boolean executeResult, Duration duration){
        Map<String, String> tags = new HashMap<>();
        tags.put("requestUrl", pjp.getSignature().getName());
        tags.put("executeResult", executeResult.toString());
        Metrics.REGISTRY.timer(id.withTags(tags)).record(duration);
    }

    private String getTenantIdByAlipay(StringBuilder logContext, ProceedingJoinPoint pjp) {
        String tenantId = "";
        String methodName = pjp.getSignature().getName();
        Object[] args = pjp.getArgs();
        for (Object object : args) {
            if (object instanceof HttpServletRequest) {
                HttpServletRequest req = (HttpServletRequest) object;
                Map<String, String[]> parameterMap = req.getParameterMap();
                //支付回调方法名
                if ("alipayNotifyReceive".equals(methodName)) {
                    LogSplicingUtils.addLogContext(logContext, "支付回调参数parameterMap：%s", JsonUtils.toJsonString(parameterMap));
                    //回调参数中有个参数名为：body得数组
                    String[] bodies = parameterMap.get("body");
                    if (bodies != null && bodies.length > 0) {
                        Map bodiesMap = JsonUtils.parseMap(bodies[0]);
                        LogSplicingUtils.addLogContext(logContext, "bodiesMap：%s", JsonUtils.toJsonString(bodiesMap));

                        Object payTenantId = bodiesMap.get("tenantId");
                        tenantId = Objects.nonNull(payTenantId) ? String.valueOf(payTenantId) : "";
                    }
                    LogSplicingUtils.addLogContext(logContext, "支付回调租户ID：%s", tenantId);
                }
            }
        }
        return tenantId;
    }

    /**
     * @param corpId
     * @param tenantAndCorpRelation
     * @return
     * @描述：获取租户ID
     * @时间：2022年01月15日 下午17:20:21
     */
    private String getTenantIdByCorpId(StringBuilder logContext, String corpId, Map<String, List<String>> tenantAndCorpRelation) {
        //TODO 当前公司ID如为null  则authkey tenantID 两个字段必须有一个存在  来判断当前租户

        LogSplicingUtils.addLogContext(logContext, "corpId: %s, tenantAndCorpRelation: %s", corpId, JsonUtils.toJsonString(tenantAndCorpRelation));
        if (!CollectionUtils.isEmpty(tenantAndCorpRelation) && StringUtils.isNotBlank(corpId)) {
            for (Map.Entry<String, List<String>> next : tenantAndCorpRelation.entrySet()) {
                if (!CollectionUtils.isEmpty(next.getValue().stream().filter(a -> Objects.equals(corpId, a)).collect(Collectors.toList()))) {
                    LogSplicingUtils.addLogContext(logContext, "corpId 对应租户ID: %s", next.getKey());
                    return next.getKey();
                }
            }
        }
        return null;
    }


    /**
     * @param pjp
     * @param logContext
     * @return
     * @描述：获取请求参数
     * @时间：2022年01月15日 下午17:20:21
     */
    private String getCorpId(ProceedingJoinPoint pjp, StringBuilder logContext) {
        Object[] args = pjp.getArgs();
        if (args == null || args.length == 0) {
            return null;
        }
        try {
            LogSplicingUtils.addLogContext(logContext, "入参args：%s", JsonUtils.toJsonString(args[0]));
            if (args[0] instanceof String) {
                LogSplicingUtils.addLogContext(logContext, "入参为String串");
                return Optional.ofNullable(JsonUtils.parse(args[0].toString(), GetTenantInfoDTO.class)).orElse(new GetTenantInfoDTO()).getCorpId();
            }
            return Optional.ofNullable(JsonUtils.convert(args[0], GetTenantInfoDTO.class)).orElse(new GetTenantInfoDTO()).getCorpId();
        } catch (Exception e) {
            log.error("{}", e);
        }
        return null;
    }
}
